#! /usr/bin/env python
"""dialer
"""

from twisted.application import service, internet
from twisted.internet import reactor, protocol, defer, task
from twisted.protocols import basic
from twisted.python import log, logfile, util
from twisted.cred import portal, checkers, credentials

from zope.interface import implements

from starpy import manager, fastagi, utilapplication, menu
import os, logging, pprint, time, sys, threading, binascii
import ConfigParser
from starpyex import asterix

from db_softswitch import db_softswitch


mylog = logging.getLogger('dialer')


dialer_config_path = '/etc/emir/dialer.ini'


class dialer:
    """ """

    def __init__(self):
        """ """

        try:
            self.m_cfgprs = ConfigParser.ConfigParser()
            self.m_cfgprs.read(dialer_config_path)

            print 'reading config file'
            self.log_cfg = {}
            self.log_cfg['log_path'] = self.m_cfgprs.get('log', 'log_path')
            self.log_cfg['open_flags'] = self.m_cfgprs.get('log', 'open_flags')
            self.log_cfg['debug_level'] = self.m_cfgprs.getint('log', 'debug_level')
            print '[log]'
            print 'log_path         = %s' % self.log_cfg['log_path']
            print 'open_flags       = %s' % self.log_cfg['open_flags']
            print 'debug_level      = %s' % self.log_cfg['debug_level']
            log.startLogging(open(self.log_cfg['log_path'], self.log_cfg['open_flags']))

            self.db_softswitch_cfg = {}
            self.db_softswitch_cfg['dbname'] = self.m_cfgprs.get('db_softswitch', 'dbname')
            self.db_softswitch_cfg['user'] = self.m_cfgprs.get('db_softswitch', 'user')
            self.db_softswitch_cfg['host'] = self.m_cfgprs.get('db_softswitch', 'host')
            self.db_softswitch_cfg['password'] = self.m_cfgprs.get('db_softswitch', 'password', True)
            print '[db_softswitch]'
            print 'dbname           = %s' % self.db_softswitch_cfg['dbname']
            print 'user             = %s' % self.db_softswitch_cfg['user']
            print 'host             = %s' % self.db_softswitch_cfg['host']
            print 'password         = %s' % self.db_softswitch_cfg['password']

            self.asterix = {}
            self.asterix['ami_server'] = self.m_cfgprs.get('asterisk', 'ami_server')
            self.asterix['ami_port'] = self.m_cfgprs.getint('asterisk', 'ami_port')
            self.asterix['ami_username'] = self.m_cfgprs.get('asterisk', 'ami_username')
            self.asterix['ami_secret'] = self.m_cfgprs.get('asterisk', 'ami_secret')
            self.asterix['ami_period'] = self.m_cfgprs.getfloat('asterisk', 'ami_period')
            self.asterix['ami_timeout'] = self.m_cfgprs.getfloat('asterisk', 'ami_timeout')
            self.asterix['fagi_interface'] = self.m_cfgprs.get('asterisk', 'fagi_interface')
            self.asterix['fagi_port'] = self.m_cfgprs.getint('asterisk', 'fagi_port')
            self.asterix['log_file'] = self.m_cfgprs.get('asterisk', 'log_file')
            self.asterix['log_tokens'] = self.m_cfgprs.get('asterisk', 'log_tokens')
            self.asterix['log_level'] = self.m_cfgprs.getint('asterisk', 'log_level')
            print '[asterix]'
            print 'ami_server     = %s' % self.asterix['ami_server']
            print 'ami_port       = %s' % self.asterix['ami_port']
            print 'ami_username   = %s' % self.asterix['ami_username']
            print 'ami_secret     = %s' % self.asterix['ami_secret']
            print 'ami_period     = %s' % self.asterix['ami_period']
            print 'ami_timeout    = %s' % self.asterix['ami_timeout']
            print 'fagi_interface = %s' % self.asterix['fagi_interface']
            print 'fagi_port      = %s' % self.asterix['fagi_port']
            print 'log_file       = %s' % self.asterix['log_file']
            print 'log_tokens     = %s' % self.asterix['log_tokens']
            print 'log_level      = %s' % self.asterix['log_level']

        except:
            print 'except reading config file'
            pass

        logging.basicConfig()
        mylog.setLevel( self.log_cfg['debug_level'] )

        self.m_asterix = asterix.asterix(ami_username=self.asterix['ami_username'],\
                                         ami_secret=self.asterix['ami_secret'],\
                                         ami_server=self.asterix['ami_server'],\
                                         ami_port=self.asterix['ami_port'],\
                                         ami_connect_handler=self.ami_connect_handler,\
                                         fagi_interface=self.asterix['fagi_interface'],\
                                         fagi_port=self.asterix['fagi_port'],\
                                         fagi_handler=self.fagi_handler,\
                                         fagi_vars=['CALLCOUNT'],\
                                         log_file=self.asterix['log_file'],\
                                         log_tokens=self.asterix['log_tokens'],\
                                         log_level=self.asterix['log_level'])

        dsn = "dbname=%(dbname)s user=%(user)s host=%(host)s password=%(password)s" % self.db_softswitch_cfg
        self.db_softswitch = db_softswitch(dsn=dsn, test_period=5.0, reconnect_handler=self.realterisk_reconnect_handler)

        self.calls = {}
        self.destcalls = {}

    def realterisk_reconnect_handler(self):
        """ realterisk_reconnect_handler """
        print 'realterisk_reconnect_handler ok'
        def onCommandOk(result):
            print 'odbc show ok: %s' % result
        def onCommandErr(reason):
            print 'odbc show err: %s' % reason
        #df = self.m_asterix.m_ami.command('reload')
        df = self.m_asterix.m_ami.command('odbc show') # !!!
        df.addCallbacks(onCommandOk, onCommandErr)
    
    def ami_connect_handler(self):
        """ ami_connect_handler """
        print 'ami_connect_handler'
        try:
            self.m_asterix.m_ami.registerEvent('Cdr', self.onCdr)
            print 'ami: registerEvent("Cdr") ok'
        except:
            print 'ami: registerEvent("Cdr") error'
        #try:
        #    self.m_asterix.m_ami.registerEvent('Dial', self.onDial)
        #    print 'ami: registerEvent("Dial") ok'
        #except:
        #    print 'ami: registerEvent("Dial") error'
        #try:
        #    self.m_asterix.m_ami.registerEvent('Hangup', self.onHangup)
        #    print 'ami: registerEvent("Hangup") ok'
        #except:
        #    print 'ami: registerEvent("Hangup") error'
        try:
            self.m_asterix.m_ami.registerEvent('UserEvent', self.onUserEvent)
            print 'ami: registerEvent("UserEvent") ok'
        except:
            print 'ami: registerEvent("UserEvent") error'
        try:
            self.m_asterix.m_ami.registerEvent("LogChannel", self.onLogChannel)
            print 'ami: registerEvent("LogChannel") ok'
        except:
            print 'ami: registerEvent("LogChannel") error'
        try:
            self.m_asterix.m_ami.registerEvent("NewJointCapability", self.onNewJointCapability)
            print 'ami: registerEvent("NewJointCapability") ok'
        except:
            print 'ami: registerEvent("NewJointCapability") error'
        try:
            self.m_asterix.m_ami.registerEvent("Dial", self.onDial)
            print 'ami: registerEvent("Dial") ok'
        except:
            print 'ami: registerEvent("Dial") error'

    def onDial(self, ami, event):
        """ onDial """
        print 'dial event: %s' % event
        call = self.calls.get(event['srcuniqueid'], None)
        if not call:
            print 'ERROR: calls not valid!!!'
            return
        call['destuniqueid'] = event['destuniqueid']
        self.destcalls[event['destuniqueid']] = call
        #self.calls[event['destuniqueid']] = call

    def onExtensionStatus(self, ami, event):
        """ onExtensionStatus """
        print 'extension status event: %s' % event

    def onLogChannel(self, ami, event):
        """ onLogChannel """
        print 'log channel event: %s' % event

    def onUserEvent(self, ami, event):
        """ onUserEvent """
        print 'user event: %s' % event
        call = self.calls.get(event['uniqueid'], None)
        #print 'call %s' % call
        if not call:
            print 'ERROR: calls not valid!!!'
            return
        trunk = call['trunks'][0]
        trunk['dialstatus'] = event['dialstatus']
        self.db_softswitch.update_dialstatus(uniqueid=event['uniqueid'], operator=trunk['operator'], dest=trunk['dest'], trunkname=trunk['trunkname'], status=trunk['dialstatus'])

    def onNewJointCapability(self, ami, event):
        """ onNewJointCapability """
        print 'newjointcapability event: %s' % event
        #call = self.calls.get(event['uniqueid'], None)
        call = self.destcalls.get(event['uniqueid'], None)
        #print 'call %s' % call
        if not call:
            print 'ERROR: calls not valid!!!'
            return
        call['combinedcodec'] = event.get('capabilities combined', None)

    def onHangup(self, ami, event):
        """ onHangup """
        print 'hangup event: %s' % event

    def onCdr(self, ami, event):
        """ onCdr """
        print 'cdr event: %s' % event
        #try:
        #    print 'self.calls %s' % self.calls
        #except:
        #    print 'ERROR: self.calls not valid!!!'
        call = self.calls.get(event['uniqueid'], None)
        #print 'call %s' % call
        if not call:
            print 'ERROR: calls not valid!!!'
            return
        trunk = call['trunks'][0]
        caller = call['caller']
        mycurprice = call['price']
        ppm = mycurprice['ppm']
        #print 'ppm %s' % ppm
        #cost = ppm * int(event['duration']) / 60.0
        if int(event['billableseconds']) > 0:
            cost = ppm * int(event['billableseconds']) / 60.0
            #print 'cost %s' % cost
            #self.db_softswitch.update_user_balance(accountcode=caller['accountcode'], cost=cost)
            self.db_softswitch.update_user_balance(callerid=caller['callerid'], cost=cost)
            if trunk.get('dialstatus', None):
                event['dialstatus'] = trunk['dialstatus']
            else:
                event['dialstatus'] = event['disposition']
            event['operator'] = trunk['operator']
            event['trunkname'] = trunk['trunkname']
            event['pricename'] = trunk['pricename']
            #dest = trunk['dest']
            event['ppm'] = trunk['ppm']
            event['mypricename'] = mycurprice['pricename']
            event['mydest'] = mycurprice['dest']
            event['myppm'] = mycurprice['ppm']
            event['combinedcodec'] = call['combinedcodec']
            self.db_softswitch.update_fullcdr(**event)
            self.db_softswitch.update_dialstatus(uniqueid=event['uniqueid'], billsec=event['billableseconds'], duration=event['duration'], operator=trunk['operator'], trunkname=trunk['trunkname'], dest=trunk['dest'])
        try:
            #del self.calls[call['destuniqueid']]
            del self.destcalls[call['destuniqueid']]
        except:
            print 'ERROR: call with destuniqueid = %s not valid!!!' % call['destuniqueid']
        try:
            del self.calls[event['uniqueid']]
        except:
            print 'ERROR: call with uniqueid = %s not valid!!!' % event['uniqueid']

    def fagi_handler(self, agi):
        """ fagi_handler """
        mylog.debug('fagi_handler()')

        def fagi_return_err(fagi_operno):
            return {'FAGI_ERRCODE': fagi_operno}

        number = agi.variables['agi_extension']
        uniqueid = agi.variables['agi_uniqueid']
        accountcode = agi.variables['agi_accountcode']
        callerid = agi.variables['agi_callerid']
        channel = agi.variables['agi_channel']
        context = agi.variables['agi_context']
        callcount = agi.variables['CALLCOUNT']
        
        mylog.debug('accountcode %s', accountcode)
        mylog.debug('uniqueid    %s', uniqueid)
        mylog.debug('number      %s', number)
        mylog.debug('callerid    %s', callerid)
        mylog.debug('channel     %s', channel)
        mylog.debug('callcount   %s', callcount)
            
        def EpilogErr():
            def onHangupOk(result):
                print 'onHangupOk %s' % result
                agi.finish()
            def onHangupErr(reason):
                print 'onHangupErr %s' % reason
                agi.finish()
            df = agi.hangup()
            df.addCallbacks(onHangupOk, onHangupErr)

        def onSetAccountCodeErr(failure):
            print 'onSetAccountCodeErr %s' % failure
            agi.finish()

        def onSetAccountCodeOk(result):
            print 'onSetAccountCodeOk %s' % result
            agi.finish()

        def SetAccountCode(accountcode):
            df = agi.execute('Set', 'CDR(accountcode)=%s' % accountcode)
            df.addCallbacks(onSetAccountCodeOk, onSetAccountCodeErr)

        if int(callcount):
            print 'callcount %s' % callcount
            call = self.calls[uniqueid]
            if len(call['trunks']) <= 1:
                EpilogErr()
                return
            if call['trunks'][0]['dialstatus'] == 'ANSWER':
                EpilogErr()
                return
            call['trunks'].pop(0)
            trunks = call['trunks']
            caller = call['caller']
            return {'ACCOUNTCODE': caller['accountcode'], 'TRUNK': trunks[0]['trunk'], 'MAXTIME': trunks[0]['maxtime'], 'CALLCOUNT': int(callcount) + 1}

        call = {}

        caller = self.db_softswitch.select_caller_info(callerid=callerid)
        print 'caller %s' % caller
        if not caller:
            EpilogErr()
            return
        if caller['balance'] <= 0.0:
            EpilogErr()
            return

        mycurprice = self.db_softswitch.select_mycurprice(number)
        if not mycurprice:
            EpilogErr()
            return
        print 'mycurprice %s' % mycurprice
        trunks = self.db_softswitch.select_callroutes(caller['tariff'], caller['tester'], number)
        #print 'trunks %s' % trunks
        for trunk in trunks:
            if trunk['techprefix']:
                fullnumber = trunk['techprefix'] + number
            else:
                fullnumber = number
            if trunk['trunktype'] == 'iax':
                trunk['trunk'] = '%s/%s/%s' % (trunk['trunktype'].upper(), trunk['trunkname'], fullnumber)
                trunk['trunk'] = trunk['trunk'].replace('IAX', 'IAX2')
            elif trunk['trunktype'] == 'sip':
                trunk['trunk'] = '%s/%s@%s' % (trunk['trunktype'].upper(), fullnumber, trunk['trunkname'])
            elif trunk['trunktype'] == 'h323':
                trunk['trunk'] = '%s/%s@%s' % (trunk['trunktype'].upper(), fullnumber, trunk['trunkname'])
            trunk['maxtime'] = int(caller['balance'] * 60.0 / float(mycurprice['ppm']))
            trunk['dialstatus'] = None
        print 'trunks %s' % trunks

        if not trunks:
            EpilogErr()
            return

        call['caller'] = caller
        call['price'] = mycurprice
        call['trunks'] = trunks
        call['combinedcodec'] = None
        call['destuniqueid'] = None
        self.calls[uniqueid] = call
        #SetAccountCode(caller['accountcode'])
        return {'ACCOUNTCODE': caller['accountcode'], 'TRUNK': trunks[0]['trunk'], 'MAXTIME': trunks[0]['maxtime'], 'CALLCOUNT': int(callcount) + 1}


    def originate(self, number, qname, callerid, retry_no):
        def onOriginateOk(result, number, qname, callerid, retry_no):
            """  """
            print 'originate (%s, %s, %s) ok retry%s!!! %s' % (number, qname, callerid, retry_no, result)
        def onOriginateErr(reason, number, qname, callerid, retry_no):
            """  """
            print 'originate (%s, %s, %s) err retry%s!!! %s' % (number, qname, callerid, retry_no, reason.getTraceback())
            retry_no += 1
            if retry_no > self.ami_cfg['max_call_retry']:
                print 'max retry!!!'
                return
            reactor.callLater(self.ami_cfg['call_retry_pause'], self.originate, number, qname, callerid, retry_no)
        print 'originate (%s, %s, %s) retry%s' % (number, qname, callerid, retry_no)
        #df = self.m_asterix.m_ami.originate('IAX2/harpoon.itconnection.ru/79214239785', 'incoming', 51, 1)
        #df = self.m_asterix.m_ami.originate('IAX2/itcspb/%s@itcspb' % number, 'incoming', 51, 1,\
        #                          60, None, None, None,\
        #                          None, {'FAGI_QNAME': qname, 'FAGI_WAVFILE': time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())})
        df = self.m_asterix.m_ami.originate('IAX2/%s/%s@%s' % (self.ami_cfg['iax_channel'], number, self.ami_cfg['iax_context']), self.ami_cfg['queue_context'], self.ami_cfg['queue_extension'], self.ami_cfg['queue_priority'],\
                                  60, callerid, None, None,\
                                  None, {'FAGI_QNAME': qname, 'FAGI_WAVFILE': time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))})

        df.addCallbacks(onOriginateOk, onOriginateErr, callbackArgs=(number, qname, callerid, retry_no,), errbackArgs=(number, qname, callerid, retry_no,))
        #return df


if __name__ == "__main__":
    dialer = dialer()
    reactor.run()
